<?php
//SAE系统函数
function sae_set_display_errors($show = true) {
	//在本地设置不显示错误， 只不显示sae_debug的文字信息， 但是会显示系统错误，方便我们开发。
	global $sae_config;
	$sae_config ['display_error'] = $show;
}
function sae_debug($log) {
	global $sae_config;
	error_log ( date ( '[c]' ) . $log . PHP_EOL, 3, $sae_config ['debug_file'] );
	if ($sae_config ['display_error'] !== false) {
		echo $log . PHP_EOL;
	}
}

function memcache_init() {
	static $handler;
	if (is_object ( $handler ))
		return $handler;
	$handler = new Memcache ();
	$handler->connect ( '127.0.0.1', 11211 );
	return $handler;
}

function sae_xhprof_start() {
	//pass
}

function sae_xhprof_end() {
	return true;
}
//向下兼容函数
function sae_image_init($ak = '', $sk = '', $image_bin = '') {
	if (! isset ( $GLOBALS ['sae_image_instance'] )) {
		$GLOBALS ['sae_image_instance'] = new SaeImage ( $image_bin );
	}
	
	return $GLOBALS ['sae_image_instance'];

}

function sae_storage_init($accesskey, $secretkey, $ssl = false) {
	if (! isset ( $GLOBALS ['sae_storage_instance'] )) {
		include_once ('sae_storage.class.php');
		$GLOBALS ['sae_storage_instance'] = new SaeStorage ( $accesskey, $secretkey );
	}
	
	return $GLOBALS ['sae_storage_instance'];
}

function sae_mysql_init($host, $port, $accesskey, $secretkey, $appname, $do_replication = true) {
	if (! isset ( $GLOBALS ['sae_mysql_instance'] )) {
		include_once ('sae_mysql.class.php');
		$GLOBALS ['sae_mysql_instance'] = new SaeMysql ();
	}
	
	return $GLOBALS ['sae_mysql_instance'];
}

//TODU 完善 fetch url
//-------------------------------------------------------------------------------------------------


function _header_info($header) {
	$hinfo = array ();
	$header_lines = explode ( "\r", trim ( $header ) );
	$first = array_shift ( $header_lines );
	// HTTP/1.1 301 Moved Permanently
	$reg = "/HTTP\/(.+?)\s([0-9]+)\s(.+)/is";
	if (preg_match ( $reg, trim ( $first ), $out )) {
		$hinfo ['version'] = $out [1];
		$hinfo ['code'] = $out [2];
		$hinfo ['code_info'] = $out [3];
	} else
		return false;
	if (is_array ( $header_lines )) {
		foreach ( $header_lines as $line ) {
			$fs = explode ( ":", trim ( $line ), 2 );
			if (strlen ( trim ( $fs [0] ) ) > 0) {
				if (isset ( $hinfo [strtolower ( trim ( $fs [0] ) )] ))
					$hinfo [strtolower ( trim ( $fs [0] ) )] = array_merge ( ( array ) $hinfo [strtolower ( trim ( $fs [0] ) )], ( array ) trim ( $fs [1] ) );
				else
					$hinfo [strtolower ( trim ( $fs [0] ) )] = trim ( $fs [1] );
			}
		}
	}
	return $hinfo;
}
//-------------------------------------------------------------------------------------------------


function _get_signature($accesskey, $securekey, &$header_array) {
	$content = "FetchUrl";
	$content .= $header_array ["FetchUrl"];
	$content .= "TimeStamp";
	$content .= $header_array ['TimeStamp'];
	$content .= "AccessKey";
	$content .= $header_array ['AccessKey'];
	return base64_encode ( hash_hmac ( 'sha256', $content, $securekey, true ) );
}
//-------------------------------------------------------------------------------------------------


function _read_header($ch, $string) {
	global $errno, $errmsg, $rheader;
	$rheader .= $string;
	$ret = explode ( " ", $string );
	if (count ( $ret ) == 3 && $ret [0] == 'HTTP/1.1') {
		if ($ret [1] == 200)
			$errno = 0;
		else {
			$errno = $ret [1];
			$errmsg = $ret [2];
		}
	}
	return strlen ( $string );
}
//-------------------------------------------------------------------------------------------------


function _read_data($ch, $string) {
	global $rdata;
	$rdata .= $string;
	return strlen ( $string );
}
//-------------------------------------------------------------------------------------------------


function _fetch_url($url, $accesskey, $securekey, &$header, &$error, $opt = NULL) {
	global $errno, $errmsg, $rheader, $rdata;
	$rheader = '';
	$rdata = '';
	$errno = 0;
	$errmsg = '';
	$ch = curl_init ();
	curl_setopt ( $ch, CURLOPT_HEADERFUNCTION, '_read_header' );
	curl_setopt ( $ch, CURLOPT_WRITEFUNCTION, '_read_data' );
	curl_setopt ( $ch, CURLOPT_CONNECTTIMEOUT, 3 );
	curl_setopt ( $ch, CURLOPT_TIMEOUT, 10 );
	$header_array = array ();
	if ($opt && is_array ( $opt )) {
		if (array_key_exists ( 'username', $opt ) && array_key_exists ( 'password', $opt ))
			curl_setopt ( $ch, CURLOPT_USERPWD, $opt ['username'] . ':' . $opt ['password'] );
		if (array_key_exists ( 'useragent', $opt ))
			curl_setopt ( $ch, CURLOPT_USERAGENT, $opt ['useragent'] );
		if (array_key_exists ( 'post', $opt )) {
			curl_setopt ( $ch, CURLOPT_POST, true );
			curl_setopt ( $ch, CURLOPT_POSTFIELDS, $opt ['post'] );
		}
		if (array_key_exists ( 'truncated', $opt ))
			$header_array ['AllowTruncated'] = $opt ['truncated'];
		
		//    if(array_key_exists('connecttimeout',$opt))
		//       $header_array['ConnectTimeout']=$opt['connecttimeout'];
		//    if(array_key_exists('sendtimeout',$opt))
		//       $header_array['SendTimeout']=$opt['sendtimeout'];
		//    if(array_key_exists('readtimeout',$opt))
		//       $header_array['ReadTimeout']=$opt['readtimeout'];
		

		if (array_key_exists ( 'headers', $opt )) {
			$headers = $opt ['headers'];
			if (is_array ( $headers )) {
				foreach ( $headers as $k => $v )
					$header_array [$k] = $v;
			}
		}
	} //end if is_array
	$header_array ['FetchUrl'] = $url;
	$header_array ['AccessKey'] = $accesskey;
	$header_array ['TimeStamp'] = date ( 'Y-m-d H:i:s' );
	$header_array ['Signature'] = _get_signature ( $accesskey, $securekey, $header_array );
	
	$header_array2 = array ();
	foreach ( $header_array as $k => $v )
		array_push ( $header_array2, $k . ': ' . $v );
	
	curl_setopt ( $ch, CURLOPT_HTTPHEADER, $header_array2 );
	curl_setopt ( $ch, CURLOPT_URL, SAE_FETCHURL_SERVICE_ADDRESS );
	curl_exec ( $ch );
	curl_close ( $ch );
	$header = $rheader;
	if ($errno == 0)
		return $rdata;
	$error = $errno . ': ' . $errmsg;
	return false;
} //end function fetchurl
//-------------------------------------------------------------------------------------------------


function fetch_url($url, $accesskey, $securekey, &$header, &$error, $opt = NULL) {
	if ($opt && is_array ( $opt ) && array_key_exists ( 'redirect', $opt ) && $opt ['redirect'] == true) {
		$times = 0;
		while ( true ) {
			$rt = _fetch_url ( $url, $accesskey, $securekey, $header, $error, $opt );
			if ($rt == false)
				return $rt;
			$info = _header_info ( $header );
			$jump = false;
			if (isset ( $info ['location'] ) && ($info ['code'] == 301 || $info ['code'] == 302) && $times < 5)
				$jump = true;
			if ($jump == true) {
				$times ++;
				$url = $info ['location'];
				continue;
			}
			return $rt;
		} //end while
	} //end if
	return _fetch_url ( $url, $accesskey, $securekey, $header, $error, $opt );
}
//-------------------------------------------------------------------------------------------------


//实现wrapper


if (! in_array ( "saemc", stream_get_wrappers () ))
	stream_wrapper_register ( "saemc", "SaeMemcacheWrapper" );

class SaeMemcacheWrapper // implements WrapperInterface
{
	public $dir_mode = 16895; //040000 + 0222;
	public $file_mode = 33279; //0100000 + 0777;
	

	public function __construct() {
		$this->mc = memcache_init ();
	}
	
	public function mc() {
		if (! isset ( $this->mc ))
			$this->mc = new Memcache ();
		return $this->mc;
	}
	
	public function stream_open($path, $mode, $options, &$opened_path) {
		$this->position = 0;
		$this->mckey = trim ( substr ( $path, 8 ) );
		$this->mode = $mode;
		$this->options = $options;
		
		if (in_array ( $this->mode, array ('r', 'r+', 'rb' ) )) {
			if ($this->mccontent = memcache_get ( $this->mc, $this->mckey )) {
				$this->get_file_info ( $this->mckey );
				$this->stat ['mode'] = $this->stat [2] = $this->file_mode;
			} else {
				trigger_error ( "fopen({$path}): failed to read from Memcached: No such key.", E_USER_WARNING );
				return false;
			}
		} elseif (in_array ( $this->mode, array ('a', 'a+', 'ab' ) )) {
			if ($this->mccontent = memcache_get ( $this->mc, $this->mckey )) {
				$this->get_file_info ( $this->mckey );
				$this->stat ['mode'] = $this->stat [2] = $this->file_mode;
				$this->position = strlen ( $this->mccontent );
			} else {
				$this->mccontent = '';
				$this->stat ['ctime'] = $this->stat [10] = time ();
			}
		} elseif (in_array ( $this->mode, array ('x', 'x+', 'xb' ) )) {
			if (! memcache_get ( $this->mc, $this->mckey )) {
				$this->mccontent = '';
				$this->statinfo_init ();
				$this->stat ['ctime'] = $this->stat [10] = time ();
			} else {
				trigger_error ( "fopen({$path}): failed to create at Memcached: Key exists.", E_USER_WARNING );
				return false;
			}
		} elseif (in_array ( $this->mode, array ('w', 'w+', 'wb' ) )) {
			$this->mccontent = '';
			$this->statinfo_init ();
			$this->stat ['ctime'] = $this->stat [10] = time ();
		} else {
			$this->mccontent = memcache_get ( $this->mc, $this->mckey );
		}
		
		return true;
	}
	
	public function stream_read($count) {
		if (in_array ( $this->mode, array ('w', 'x', 'a', 'wb', 'xb', 'ab' ) )) {
			return false;
		}
		
		$ret = substr ( $this->mccontent, $this->position, $count );
		$this->position += strlen ( $ret );
		
		$this->stat ['atime'] = $this->stat [8] = time ();
		$this->stat ['uid'] = $this->stat [4] = 0;
		$this->stat ['gid'] = $this->stat [5] = 0;
		
		return $ret;
	}
	
	public function stream_write($data) {
		if (in_array ( $this->mode, array ('r', 'rb' ) )) {
			return false;
		}
		
		$left = substr ( $this->mccontent, 0, $this->position );
		$right = substr ( $this->mccontent, $this->position + strlen ( $data ) );
		$this->mccontent = $left . $data . $right;
		
		if (memcache_set ( $this->mc, $this->mckey, $this->mccontent )) {
			$this->stat ['mtime'] = $this->stat [9] = time ();
			$this->position += strlen ( $data );
			return $this->stat ['size'] = $this->stat [7] = strlen ( $data );
		} else
			return false;
	}
	
	public function stream_close() {
		
		memcache_set ( $this->mc, $this->mckey . '.meta', serialize ( $this->stat ) );
	
		//memcache_close( $this->mc );
	}
	
	public function stream_eof() {
		
		return $this->position >= strlen ( $this->mccontent );
	}
	
	public function stream_tell() {
		
		return $this->position;
	}
	
	public function stream_seek($offset, $whence = SEEK_SET) {
		
		switch ($whence) {
			case SEEK_SET :
				
				if ($offset < strlen ( $this->mccontent ) && $offset >= 0) {
					$this->position = $offset;
					return true;
				} else
					return false;
				
				break;
			
			case SEEK_CUR :
				
				if ($offset >= 0) {
					$this->position += $offset;
					return true;
				} else
					return false;
				
				break;
			
			case SEEK_END :
				
				if (strlen ( $this->mccontent ) + $offset >= 0) {
					$this->position = strlen ( $this->mccontent ) + $offset;
					return true;
				} else
					return false;
				
				break;
			
			default :
				
				return false;
		}
	}
	
	public function stream_stat() {
		return $this->stat;
	}
	
	// ============================================
	public function mkdir($path, $mode, $options) {
		$path = trim ( substr ( $path, 8 ) );
		
		//echo "回调mkdir\n";
		$path = rtrim ( $path, '/' );
		
		$this->stat = $this->get_file_info ( $path );
		$this->stat ['ctime'] = $this->stat [10] = time ();
		$this->stat ['mode'] = $this->stat [2] = $this->dir_mode;
		
		//echo "生成新的stat数据" . print_r( $this->stat , 1 );
		

		memcache_set ( $this->mc (), $path . '.meta', serialize ( $this->stat ) );
		
		//echo "写入MC. key= " . $path.'.meta ' .  memcache_get( $this->mc , $path.'.meta'  );
		memcache_close ( $this->mc );
		
		return true;
	}
	
	public function rename($path_from, $path_to) {
		$path_from = trim ( substr ( $path_from, 8 ) );
		$path_to = trim ( substr ( $path_to, 8 ) );
		
		memcache_set ( $this->mc (), $path_to, memcache_get ( $this->mc (), $path_from ) );
		memcache_set ( $this->mc (), $path_to . '.meta', memcache_get ( $this->mc (), $path_from . '.meta' ) );
		memcache_delete ( $this->mc (), $path_from );
		memcache_delete ( $this->mc (), $path_from . '.meta' );
		clearstatcache ( true );
		return true;
	}
	
	public function rmdir($path, $options) {
		$path = trim ( substr ( $path, 8 ) );
		
		$path = rtrim ( $path, '/' );
		
		memcache_delete ( $this->mc (), $path . '.meta' );
		clearstatcache ( true );
		return true;
	}
	
	public function unlink($path) {
		$path = trim ( substr ( $path, 8 ) );
		$path = rtrim ( $path, '/' );
		
		memcache_delete ( $this->mc (), $path );
		memcache_delete ( $this->mc (), $path . '.meta' );
		clearstatcache ( true );
		return true;
	}
	
	public function url_stat($path, $flags) {
		$path = trim ( substr ( $path, 8 ) );
		$path = rtrim ( $path, '/' );
		
		if (! $this->is_file_info_exists ( $path )) {
			return false;
		} else {
			if ($stat = memcache_get ( $this->mc (), $path . '.meta' )) {
				$this->stat = unserialize ( $stat );
				if (is_array ( $this->stat )) {
					if ($this->stat ['mode'] == $this->dir_mode || $c = memcache_get ( $this->mc (), $path )) {
						return $this->stat;
					} else {
						memcache_delete ( $this->mc (), $path . '.meta' );
					}
				}
			}
			return false;
		}
	}
	
	// ============================================
	

	public function is_file_info_exists($path) {
		//echo "获取MC数据 key= " .  $path.'.meta' ;
		$d = memcache_get ( $this->mc (), $path . '.meta' );
		//echo "\n返回数据为" . $d . "\n";
		return $d;
	}
	
	public function get_file_info($path) {
		if ($stat = memcache_get ( $this->mc (), $path . '.meta' ))
			return $this->stat = unserialize ( $stat );
		else
			$this->statinfo_init ();
	}
	
	public function statinfo_init($is_file = true) {
		$this->stat ['dev'] = $this->stat [0] = 0x8002;
		$this->stat ['ino'] = $this->stat [1] = mt_rand ( 10000, PHP_INT_MAX );
		
		if ($is_file)
			$this->stat ['mode'] = $this->stat [2] = $this->file_mode;
		else
			$this->stat ['mode'] = $this->stat [2] = $this->dir_mode;
		
		$this->stat ['nlink'] = $this->stat [3] = 0;
		$this->stat ['uid'] = $this->stat [4] = 0;
		$this->stat ['gid'] = $this->stat [5] = 0;
		$this->stat ['rdev'] = $this->stat [6] = 0;
		$this->stat ['size'] = $this->stat [7] = 0;
		$this->stat ['atime'] = $this->stat [8] = 0;
		$this->stat ['mtime'] = $this->stat [9] = 0;
		$this->stat ['ctime'] = $this->stat [10] = 0;
		$this->stat ['blksize'] = $this->stat [11] = 0;
		$this->stat ['blocks'] = $this->stat [12] = 0;
	
	}
	
	public function dir_closedir() {
		return false;
	}
	
	public function dir_opendir($path, $options) {
		return false;
	}
	
	public function dir_readdir() {
		return false;
	}
	
	public function dir_rewinddir() {
		return false;
	}
	
	public function stream_cast($cast_as) {
		return false;
	}
	
	public function stream_flush() {
		return false;
	}
	
	public function stream_lock($operation) {
		return false;
	}
	
	public function stream_set_option($option, $arg1, $arg2) {
		return false;
	}

}

/* BEGIN *******************  Storage Wrapper By Elmer Zhang At 16/Mar/2010 14:47 ****************/

class SaeStorageWrapper // implements WrapperInterface
{
	private $writen = true;
	
	public function __construct() {
		$this->stor = new SaeStorage ();
	}
	
	public function stor() {
		if (! isset ( $this->stor ))
			$this->stor = new SaeStorage ();
	}
	
	public function stream_open($path, $mode, $options, &$opened_path) {
		$pathinfo = parse_url ( $path );
		$this->domain = $pathinfo ['host'];
		$this->file = ltrim ( strstr ( $path, $pathinfo ['path'] ), '/\\' );
		$this->position = 0;
		$this->mode = $mode;
		$this->options = $options;
		
		// print_r("OPEN\tpath:{$path}\tmode:{$mode}\toption:{$option}\topened_path:{$opened_path}\n");
		

		if (in_array ( $this->mode, array ('r', 'r+', 'rb' ) )) {
			if ($this->fcontent = $this->stor->read ( $this->domain, $this->file )) {
			} else {
				trigger_error ( "fopen({$path}): failed to read from Storage: No such domain or file.", E_USER_WARNING );
				return false;
			}
		} elseif (in_array ( $this->mode, array ('a', 'a+', 'ab' ) )) {
			trigger_error ( "fopen({$path}): Sorry, saestor does not support appending", E_USER_WARNING );
			if ($this->fcontent = $this->stor->read ( $this->domain, $this->file )) {
			} else {
				trigger_error ( "fopen({$path}): failed to read from Storage: No such domain or file.", E_USER_WARNING );
				return false;
			}
		} elseif (in_array ( $this->mode, array ('x', 'x+', 'xb' ) )) {
			if (! $this->stor->getAttr ( $this->domain, $this->file )) {
				$this->fcontent = '';
			} else {
				trigger_error ( "fopen({$path}): failed to create at Storage: File exists.", E_USER_WARNING );
				return false;
			}
		} elseif (in_array ( $this->mode, array ('w', 'w+', 'wb' ) )) {
			$this->fcontent = '';
		} else {
			$this->fcontent = $this->stor->read ( $this->domain, $this->file );
		}
		
		return true;
	}
	
	public function stream_read($count) {
		if (in_array ( $this->mode, array ('w', 'x', 'a', 'wb', 'xb', 'ab' ) )) {
			return false;
		}
		
		$ret = substr ( $this->fcontent, $this->position, $count );
		$this->position += strlen ( $ret );
		
		return $ret;
	}
	
	public function stream_write($data) {
		if (in_array ( $this->mode, array ('r', 'rb' ) )) {
			return false;
		}
		
		// print_r("WRITE\tcontent:".strlen($this->fcontent)."\tposition:".$this->position."\tdata:".strlen($data)."\n");
		

		$left = substr ( $this->fcontent, 0, $this->position );
		$right = substr ( $this->fcontent, $this->position + strlen ( $data ) );
		$this->fcontent = $left . $data . $right;
		
		//if ( $this->stor->write( $this->domain, $this->file, $this->fcontent ) ) {
		$this->position += strlen ( $data );
		if (strlen ( $data ) > 0)
			$this->writen = false;
		
		return strlen ( $data );
	
		//}
	//else return false;
	}
	
	public function stream_close() {
		if (! $this->writen) {
			$this->stor->write ( $this->domain, $this->file, $this->fcontent );
			$this->writen = true;
		}
	}
	
	public function stream_eof() {
		
		return $this->position >= strlen ( $this->fcontent );
	}
	
	public function stream_tell() {
		
		return $this->position;
	}
	
	public function stream_seek($offset, $whence = SEEK_SET) {
		
		switch ($whence) {
			case SEEK_SET :
				
				if ($offset < strlen ( $this->fcontent ) && $offset >= 0) {
					$this->position = $offset;
					return true;
				} else
					return false;
				
				break;
			
			case SEEK_CUR :
				
				if ($offset >= 0) {
					$this->position += $offset;
					return true;
				} else
					return false;
				
				break;
			
			case SEEK_END :
				
				if (strlen ( $this->fcontent ) + $offset >= 0) {
					$this->position = strlen ( $this->fcontent ) + $offset;
					return true;
				} else
					return false;
				
				break;
			
			default :
				
				return false;
		}
	}
	
	public function unlink($path) {
		self::stor ();
		$pathinfo = parse_url ( $path );
		$this->domain = $pathinfo ['host'];
		$this->file = ltrim ( strstr ( $path, $pathinfo ['path'] ), '/\\' );
		
		clearstatcache ( true );
		return $this->stor->delete ( $this->domain, $this->file );
	}
	
	public function stream_flush() {
		if (! $this->writen) {
			$this->stor->write ( $this->domain, $this->file, $this->fcontent );
			$this->writen = true;
		}
		
		return $this->writen;
	}
	
	public function stream_stat() {
		return array ();
	}
	
	public function url_stat($path, $flags) {
		self::stor ();
		$pathinfo = parse_url ( $path );
		$this->domain = $pathinfo ['host'];
		$this->file = ltrim ( strstr ( $path, $pathinfo ['path'] ), '/\\' );
		
		if ($attr = $this->stor->getAttr ( $this->domain, $this->file )) {
			$stat = array ();
			$stat ['dev'] = $stat [0] = 0x8001;
			$stat ['ino'] = $stat [1] = 0;
			;
			$stat ['mode'] = $stat [2] = 33279; //0100000 + 0777;
			$stat ['nlink'] = $stat [3] = 0;
			$stat ['uid'] = $stat [4] = 0;
			$stat ['gid'] = $stat [5] = 0;
			$stat ['rdev'] = $stat [6] = 0;
			$stat ['size'] = $stat [7] = $attr ['length'];
			$stat ['atime'] = $stat [8] = 0;
			$stat ['mtime'] = $stat [9] = $attr ['datetime'];
			$stat ['ctime'] = $stat [10] = $attr ['datetime'];
			$stat ['blksize'] = $stat [11] = 0;
			$stat ['blocks'] = $stat [12] = 0;
			return $stat;
		} else {
			return false;
		}
	}
	
	public function dir_closedir() {
		return false;
	}
	
	public function dir_opendir($path, $options) {
		return false;
	}
	
	public function dir_readdir() {
		return false;
	}
	
	public function dir_rewinddir() {
		return false;
	}
	
	public function mkdir($path, $mode, $options) {
		return false;
	}
	
	public function rename($path_from, $path_to) {
		return false;
	}
	
	public function rmdir($path, $options) {
		return false;
	}
	
	public function stream_cast($cast_as) {
		return false;
	}
	
	public function stream_lock($operation) {
		return false;
	}
	
	public function stream_set_option($option, $arg1, $arg2) {
		return false;
	}

}

if (in_array ( "saestor", stream_get_wrappers () )) {
	stream_wrapper_unregister ( "saestor" );
}
stream_wrapper_register ( "saestor", "SaeStorageWrapper" ) or die ( "Failed to register protocol" );

/* END *********************  Storage Wrapper By Elmer Zhang At 16/Mar/2010 14:47 ****************/

/* BEGIN *******************  KVDB Wrapper By Elmer Zhang At 12/Dec/2011 12:37 ****************/

class SaeKVWrapper // implements WrapperInterface
{
	private $dir_mode = 16895; //040000 + 0222;
	private $file_mode = 33279; //0100000 + 0777;
	

	public function __construct() {
	}
	
	private function kv() {
		if (! isset ( $this->kv ))
			$this->kv = new SaeKV ();
		$this->kv->init ();
		return $this->kv;
	}
	
	private function open($key) {
		$value = $this->kv ()->get ( $key );
		if ($value !== false && $this->unpack_stat ( substr ( $value, 0, 20 ) ) === true) {
			$this->kvcontent = substr ( $value, 20 );
			return true;
		} else {
			return false;
		}
	}
	
	private function save($key) {
		$this->stat ['mtime'] = $this->stat [9] = time ();
		if (isset ( $this->kvcontent )) {
			$this->stat ['size'] = $this->stat [7] = strlen ( $this->kvcontent );
			$value = $this->pack_stat () . $this->kvcontent;
		} else {
			$this->stat ['size'] = $this->stat [7] = 0;
			$value = $this->pack_stat ();
		}
		return $this->kv ()->set ( $key, $value );
	}
	
	private function unpack_stat($str) {
		$arr = unpack ( "L5", $str );
		
		// check if valid
		if ($arr [1] < 10000)
			return false;
		if (! in_array ( $arr [2], array ($this->dir_mode, $this->file_mode ) ))
			return false;
		if ($arr [4] > time ())
			return false;
		if ($arr [5] > time ())
			return false;
		
		$this->stat ['dev'] = $this->stat [0] = 0x8003;
		$this->stat ['ino'] = $this->stat [1] = $arr [1];
		$this->stat ['mode'] = $this->stat [2] = $arr [2];
		$this->stat ['nlink'] = $this->stat [3] = 0;
		$this->stat ['uid'] = $this->stat [4] = 0;
		$this->stat ['gid'] = $this->stat [5] = 0;
		$this->stat ['rdev'] = $this->stat [6] = 0;
		$this->stat ['size'] = $this->stat [7] = $arr [3];
		$this->stat ['atime'] = $this->stat [8] = 0;
		$this->stat ['mtime'] = $this->stat [9] = $arr [4];
		$this->stat ['ctime'] = $this->stat [10] = $arr [5];
		$this->stat ['blksize'] = $this->stat [11] = 0;
		$this->stat ['blocks'] = $this->stat [12] = 0;
		
		return true;
	}
	
	private function pack_stat() {
		$str = pack ( "LLLLL", $this->stat ['ino'], $this->stat ['mode'], $this->stat ['size'], $this->stat ['ctime'], $this->stat ['mtime'] );
		return $str;
	}
	
	public function stream_open($path, $mode, $options, &$opened_path) {
		$this->position = 0;
		$this->kvkey = rtrim ( trim ( substr ( trim ( $path ), 8 ) ), '/' );
		$this->mode = $mode;
		$this->options = $options;
		
		if (in_array ( $this->mode, array ('r', 'r+', 'rb' ) )) {
			if ($this->open ( $this->kvkey ) === false) {
				trigger_error ( "fopen({$path}): No such key in KVDB.", E_USER_WARNING );
				return false;
			}
		} elseif (in_array ( $this->mode, array ('a', 'a+', 'ab' ) )) {
			if ($this->open ( $this->kvkey ) === true) {
				$this->position = strlen ( $this->kvcontent );
			} else {
				$this->kvcontent = '';
				$this->statinfo_init ();
			}
		} elseif (in_array ( $this->mode, array ('x', 'x+', 'xb' ) )) {
			if ($this->open ( $this->kvkey ) === false) {
				$this->kvcontent = '';
				$this->statinfo_init ();
			} else {
				trigger_error ( "fopen({$path}): Key exists in KVDB.", E_USER_WARNING );
				return false;
			}
		} elseif (in_array ( $this->mode, array ('w', 'w+', 'wb' ) )) {
			$this->kvcontent = '';
			$this->statinfo_init ();
		} else {
			$this->open ( $this->kvkey );
		}
		
		return true;
	}
	
	public function stream_read($count) {
		if (in_array ( $this->mode, array ('w', 'x', 'a', 'wb', 'xb', 'ab' ) )) {
			return false;
		}
		
		$ret = substr ( $this->kvcontent, $this->position, $count );
		$this->position += strlen ( $ret );
		
		return $ret;
	}
	
	public function stream_write($data) {
		if (in_array ( $this->mode, array ('r', 'rb' ) )) {
			return false;
		}
		
		$left = substr ( $this->kvcontent, 0, $this->position );
		$right = substr ( $this->kvcontent, $this->position + strlen ( $data ) );
		$this->kvcontent = $left . $data . $right;
		
		if ($this->save ( $this->kvkey ) === true) {
			$this->position += strlen ( $data );
			return strlen ( $data );
		} else
			return false;
	}
	
	public function stream_close() {
		$this->save ( $this->kvkey );
	}
	
	public function stream_eof() {
		
		return $this->position >= strlen ( $this->kvcontent );
	}
	
	public function stream_tell() {
		
		return $this->position;
	}
	
	public function stream_seek($offset, $whence = SEEK_SET) {
		
		switch ($whence) {
			case SEEK_SET :
				
				if ($offset < strlen ( $this->kvcontent ) && $offset >= 0) {
					$this->position = $offset;
					return true;
				} else
					return false;
				
				break;
			
			case SEEK_CUR :
				
				if ($offset >= 0) {
					$this->position += $offset;
					return true;
				} else
					return false;
				
				break;
			
			case SEEK_END :
				
				if (strlen ( $this->kvcontent ) + $offset >= 0) {
					$this->position = strlen ( $this->kvcontent ) + $offset;
					return true;
				} else
					return false;
				
				break;
			
			default :
				
				return false;
		}
	}
	
	public function stream_stat() {
		return $this->stat;
	}
	
	// ============================================
	public function mkdir($path, $mode, $options) {
		$path = rtrim ( trim ( substr ( trim ( $path ), 8 ) ), '/' );
		
		if ($this->open ( $path ) === false) {
			$this->statinfo_init ( false );
			return $this->save ( $path );
		} else {
			trigger_error ( "mkdir({$path}): Key exists in KVDB.", E_USER_WARNING );
			return false;
		}
	}
	
	public function rename($path_from, $path_to) {
		$path_from = rtrim ( trim ( substr ( trim ( $path_from ), 8 ) ), '/' );
		$path_to = rtrim ( trim ( substr ( trim ( $path_to ), 8 ) ), '/' );
		
		if ($this->open ( $path_from ) === true) {
			clearstatcache ( true );
			return $this->save ( $path_to );
		} else {
			trigger_error ( "rename({$path_from}, {$path_to}): No such key in KVDB.", E_USER_WARNING );
			return false;
		}
	}
	
	public function rmdir($path, $options) {
		$path = rtrim ( trim ( substr ( trim ( $path ), 8 ) ), '/' );
		
		clearstatcache ( true );
		return $this->kv ()->delete ( $path );
	}
	
	public function unlink($path) {
		$path = rtrim ( trim ( substr ( trim ( $path ), 8 ) ), '/' );
		
		clearstatcache ( true );
		return $this->kv ()->delete ( $path );
	}
	
	public function url_stat($path, $flags) {
		$path = rtrim ( trim ( substr ( trim ( $path ), 8 ) ), '/' );
		
		if ($this->open ( $path ) !== false) {
			return $this->stat;
		} else {
			return false;
		}
	}
	
	// ============================================
	

	private function statinfo_init($is_file = true) {
		$this->stat ['dev'] = $this->stat [0] = 0x8003;
		$this->stat ['ino'] = $this->stat [1] = crc32 ( SAE_APPNAME . '/' . $this->kvkey );
		
		if ($is_file)
			$this->stat ['mode'] = $this->stat [2] = $this->file_mode;
		else
			$this->stat ['mode'] = $this->stat [2] = $this->dir_mode;
		
		$this->stat ['nlink'] = $this->stat [3] = 0;
		$this->stat ['uid'] = $this->stat [4] = 0;
		$this->stat ['gid'] = $this->stat [5] = 0;
		$this->stat ['rdev'] = $this->stat [6] = 0;
		$this->stat ['size'] = $this->stat [7] = 0;
		$this->stat ['atime'] = $this->stat [8] = 0;
		$this->stat ['mtime'] = $this->stat [9] = time ();
		$this->stat ['ctime'] = $this->stat [10] = 0;
		$this->stat ['blksize'] = $this->stat [11] = 0;
		$this->stat ['blocks'] = $this->stat [12] = 0;
	
	}
	
	public function dir_closedir() {
		return false;
	}
	
	public function dir_opendir($path, $options) {
		return false;
	}
	
	public function dir_readdir() {
		return false;
	}
	
	public function dir_rewinddir() {
		return false;
	}
	
	public function stream_cast($cast_as) {
		return false;
	}
	
	public function stream_flush() {
		return false;
	}
	
	public function stream_lock($operation) {
		return false;
	}
	
	public function stream_set_option($option, $arg1, $arg2) {
		return false;
	}

}

if (! in_array ( "saekv", stream_get_wrappers () ))
	stream_wrapper_register ( "saekv", "SaeKVWrapper" );
	
/* END *********************  KVDB Wrapper By Elmer Zhang At 12/Dec/2011 12:37 ****************/

/* START *********************  Supported for AppCookie By Elmer Zhang At 13/Jun/2010 15:49 ****************/
$appSettings = array ();
if (isset ( $_SERVER ['HTTP_APPCOOKIE'] ) && $_SERVER ['HTTP_APPCOOKIE']) {
	$appCookie = trim ( $_SERVER ['HTTP_APPCOOKIE'] );
	$tmpSettings = array_filter ( explode ( ';', $appCookie ) );
	if ($tmpSettings) {
		foreach ( $tmpSettings as $setting ) {
			$tmp = explode ( '=', $setting );
			$appSettings [$tmp [0]] = $tmp [1];
		}
	}
}

if (isset ( $appSettings ['xhprof'] ) && in_array ( $_SERVER ['HTTP_APPVERSION'], explode ( ',', $appSettings ['xhprof'] ) )) {
	sae_xhprof_start ();
	register_shutdown_function ( "sae_xhprof_end" );
}

if (isset ( $appSettings ['debug'] ) && in_array ( $_SERVER ['HTTP_APPVERSION'], explode ( ',', $appSettings ['debug'] ) )) {
	sae_set_display_errors ( true );
}

unset ( $appSettings );
unset ( $appCookie );
unset ( $tmpSettings );
unset ( $tmp );
